package logic;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.Rectangle2D;
import java.util.Vector;

import logic.server.Player;

import util.Util;


public class Scene {
	protected Vector<CircleObject> objects = new Vector<CircleObject>();
	protected float airRes = 0.98f;
	//protected Rectangle2D bounds = new Rectangle2D.Float(0, 0, 800, 500);
	protected float bX = 1000.0f, bY = 500.0f;
	protected float boundBounce = 0.6f;
	
	protected float g1X = 100.0f, g2X = bX - 100.0f, gY1 = bY / 2 - 80.0f, gY2 = bY / 2 + 80.0f;
	protected SceneListener listener = null;
	
	private Player p1, p2;
	private CircleObject ball;
	
	private CircleObject gp11, gp12, gp21, gp22;
	
	protected int score1 = 0, score2 = 0, resetTimer = 0;
	
	public int getResetTimer() {
		return resetTimer;
	}

	public void setSceneListener(SceneListener listener)
	{
		this.listener = listener;
	}
	
	public Scene()
	{
		p1 = new Player("Crveni", 0);
		addObject(p1, 100, bY / 2);
		p1.setMass(5.0f);
		p1.setColor(new Color(0xFF6060));
		p1.setFriction(0.93f);
		p1.setParent(this);
		
		p2 = new Player("Plavi", 1);
		addObject(p2, bX - 100, bY / 2);
		p2.setMass(5.0f);
		//p2.setColor(Color.blue);
		p2.setColor(new Color(0x6060FF));
		p2.setFriction(0.93f);
		p2.setParent(this);
		
		ball = new CircleObject(200, 100, 10);
		ball.setMass(1.0f);
		addObject(ball, bX / 2, bY / 2);
		
		gp11 = new CircleObject(0, 0, 15);
		gp11.setColor(Color.white);
		gp11.setStatic();
		addObject(gp11, g1X, gY1);
		
		gp12 = new CircleObject(0, 0, 15);
		gp12.setColor(Color.white);
		gp12.setStatic();
		addObject(gp12, g1X, gY2);
		
		gp21 = new CircleObject(0, 0, 15);
		gp21.setColor(Color.white);
		gp21.setStatic();
		addObject(gp21, g2X, gY1);
		
		gp22 = new CircleObject(0, 0, 15);
		gp22.setColor(Color.white);
		gp22.setStatic();
		addObject(gp22, g2X, gY2);
	}
	
	public void addObject(CircleObject go, float pX, float pY)
	{
		go.setPosition(pX, pY);
		objects.add(go);
	}
	
	public float getAirRes()
	{
		return airRes;
	}
	
	public Vector<CircleObject> getObjects()
	{
		return objects;
	}
	
	private void collisionCheck()
	{
		//boolean anotherCheck = true;
		//int iters = 10;
		
		
		
		//while(anotherCheck && (iters > 0))
		{
			//iters--;
			//anotherCheck = false;
			
			for(int i = 0; i < objects.size() - 1; ++i)
			{
				CircleObject o1 = objects.get(i);
				for(int j = i + 1; j < objects.size(); ++j)
				{
					CircleObject o2 = objects.get(j);
					
					
					if(CircleObject.collideCircles((CircleObject)o1, (CircleObject)o2))
					{
						//anotherCheck = true;
					}
				}
			}
		}
		
		fixCircles();
	}
	
	private void fixCircles()
	{
		for(int i = 0; i < objects.size() - 1; ++i)
		{
			CircleObject o1 = objects.get(i);
			for(int j = i + 1; j < objects.size(); ++j)
			{
				CircleObject o2 = objects.get(j);
				
				while(o1.pos.distanceTo(o2.pos) <= (o1.rad + o2.rad) && (o1.recMass > 0.0f || o2.recMass > 0.0f))
				{
					Vec2D dir = o1.pos.sub(o2.pos);
					dir.normalize();
					dir._mul(0.1f);
					if(o1.recMass > 0.0f) o1.pos._add(dir);
					dir = dir.getInverse();
					if(o2.recMass > 0.0f) o2.pos._add(dir);
					
					//System.out.println("FIX!");
				}
			}
		}
	}
	
	public void update()
	{
		collisionCheck();
		
		padding(100.0f, 0.1f);
		
		for(CircleObject obj : objects)
		{
			if(obj == ball && resetTimer == 0)
			{
				
				if((obj.pos.x > g1X) && (obj.pos.x + obj.dir.x) < g1X &&
						(obj.pos.y > gY1) && (obj.pos.y < gY2))
				{
					//System.out.println("Left goal!");
					score2++;
					resetTimer = 100;
					if(listener != null) listener.goal(0);
				}
				
				if(obj.pos.x < g2X && obj.pos.x + obj.dir.x > g2X &&
						obj.pos.y > gY1 && obj.pos.y < gY2)
				{
					//System.out.println("Right goal!");
					score1++;
					resetTimer = 100;
					if(listener != null) listener.goal(1);
				}
			}
			obj.pos._add(obj.dir);
			//obj.dir._mul(airRes);
			obj.dir._mul(obj.getFriction());
			obj.checkBounds(0.0f, 0.0f, bX, bY, boundBounce);
			obj.update();
		}
		
		if(ball.pos.x > 100.0f && ball.pos.x < bX - 100.0f && (ball.pos.y < gY1 || ball.pos.y > gY2))
			ball.checkBounds(100.0f, 50.0f, bX - 100.0f, bY - 50.0f, boundBounce);
		
		if(resetTimer > 0)
		{
			resetTimer--;
			if(resetTimer == 0)
				reset();
		}
		//collisionCheck();
	}
	
	public void padding(float val, float force)
	{
		for(CircleObject obj : objects)
		{
			if(obj.pos.x < val && obj.pos.y < val)
			{
				float nf = Util.lerp(0.0f, force, Math.max(obj.pos.x, obj.pos.y) / val);
				obj.applyForce(nf, nf);
			}
			
			if(obj.pos.x < val && obj.pos.y > bY - val)
			{
				float nf = Util.lerp(0.0f, force, Math.max(obj.pos.x, bY - obj.pos.y) / val);
				obj.applyForce(nf, -nf);
			}
			
			if(obj.pos.x > bX - val && obj.pos.y < val)
			{
				float nf = Util.lerp(0.0f, force, Math.max(bY - obj.pos.x, obj.pos.y) / val);
				obj.applyForce(-nf, nf);
			}
			
			if(obj.pos.x > bX - val && obj.pos.y > bY - val)
			{
				float nf = Util.lerp(0.0f, force, Math.max(bX - obj.pos.x, bY - obj.pos.y) / val);
				obj.applyForce(-nf, -nf);
			}
		}
	}
	
	private void reset()
	{
		p1.setPosition(100.0f, bY / 2);
		p2.setPosition(bX - 100.0f, bY / 2);
		p1.dir._set(0.0f, 0.0f);
		p2.dir._set(0.0f, 0.0f);
		ball.dir._set(0.0f, 0.0f);
		ball.setPosition(bX / 2, bY / 2 - 25.0f + Util.rnd.nextInt(50));
	}
	
	public Dimension getSize()
	{
		return new Dimension((int)bX, (int)bY);
	}
	
	public int getScore1() { return score1; }
	public int getScore2() { return score2; }

	public Player getP1() {
		return p1;
	}

	public Player getP2() {
		return p2;
	}

	public CircleObject getBall() {
		return ball;
	}
}
